<template>
  <el-form
    v-show="getShow"
    ref="formLogin"
    :model="loginFromData"
    :rules="LoginRules"
    class="login-form"
    label-position="top"
    label-width="120px"
    size="large"
  >
    <el-row style="margin-right: -10px; margin-left: -10px">
      <el-col :span="24" style="padding-right: 10px; padding-left: 10px">
        <el-form-item>
          <LoginFormTitle style="width: 100%" />
        </el-form-item>
      </el-col>
      <el-col :span="24" style="padding-right: 10px; padding-left: 10px">
        <el-form-item v-if="loginFormInitdata.TenantEnable" prop="TenantName">
          <el-input
            v-model="loginFromData.TenantName"
            :placeholder="t('login.tenantNamePlaceholder')"
            :prefix-icon="iconHouse"
            link
            type="primary"
          />
        </el-form-item>
      </el-col>
      <el-col :span="24" style="padding-right: 10px; padding-left: 10px">
        <el-form-item prop="UserName">
          <el-input
            v-model="loginFromData.UserName"
            :placeholder="t('login.usernamePlaceholder')"
            :prefix-icon="iconAvatar"
          />
        </el-form-item>
      </el-col>
      <el-col :span="24" style="padding-right: 10px; padding-left: 10px">
        <el-form-item prop="PassWord">
          <el-input
            v-model="loginFromData.PassWord"
            :placeholder="t('login.passwordPlaceholder')"
            :prefix-icon="iconLock"
            show-password
            type="password"
            @keyup.enter="getCode()"
          />
        </el-form-item>
      </el-col>
      <el-col
        :span="24"
        style="padding-right: 10px; padding-left: 10px; margin-top: -20px; margin-bottom: -20px"
      >
        <el-form-item>
          <el-row justify="space-between" style="width: 100%">
            <el-col :span="6">
              <el-checkbox v-model="loginFormInitdata.RememberMe">
                {{ t('login.remember') }}
              </el-checkbox>
            </el-col>
            <el-col :offset="6" :span="12">
              <el-link style="float: right" type="primary">{{ t('login.forgetPassword') }}</el-link>
            </el-col>
          </el-row>
        </el-form-item>
      </el-col>
      <el-col :span="24" style="padding-right: 10px; padding-left: 10px">
        <el-form-item>
          <XButton
            :loading="loginLoading"
            :title="t('login.login')"
            class="w-[100%]"
            type="primary"
            @click="getCode()"
          />
        </el-form-item>
      </el-col>
      <ClickCaptcha
        ref="clickCaptchaInstance"
        :config="captchaConf"
        :events="captchaEvents"
        :data="captchaData"
      />
      <el-col :span="24" style="padding-right: 10px; padding-left: 10px">
        <el-form-item>
          <el-row :gutter="5" justify="space-between" style="width: 100%">
            <el-col :span="8">
              <XButton
                :title="t('login.btnMobile')"
                class="w-[100%]"
                @click="setLoginState(LoginStateEnum.MOBILE)"
              />
            </el-col>
            <el-col :span="8">
              <XButton
                :title="t('login.btnQRCode')"
                class="w-[100%]"
                @click="setLoginState(LoginStateEnum.QR_CODE)"
              />
            </el-col>
            <el-col :span="8">
              <XButton
                :title="t('login.btnRegister')"
                class="w-[100%]"
                @click="setLoginState(LoginStateEnum.REGISTER)"
              />
            </el-col>
          </el-row>
        </el-form-item>
      </el-col>
      <el-divider content-position="center">{{ t('login.otherLogin') }}</el-divider>
      <el-col :span="24" style="padding-right: 10px; padding-left: 10px">
        <el-form-item>
          <div class="w-[100%] flex justify-between">
            <Icon
              v-for="(item, key) in socialList"
              :key="key"
              :icon="item.icon"
              :size="30"
              class="anticon cursor-pointer"
              color="#999"
              @click="doSocialLogin(item.type)"
            />
          </div>
        </el-form-item>
      </el-col>
    </el-row>
  </el-form>
</template>
<script lang="ts" setup>
import { ElLoading, FormInstance } from 'element-plus'
import LoginFormTitle from './LoginFormTitle.vue'
import type { RouteLocationNormalizedLoaded } from 'vue-router'
import { useIcon } from '@/hooks/web/useIcon'
import { usePermissionStore } from '@/store/modules/permission'
import * as LoginApi from '@/api/login'
import { LoginStateEnum, useFormValid, useLoginState } from './useLogin'
import { QueryTenantByNameReq } from '@/models/auth/TenantInfoModel'
import cache from '@/utils/cache'
import { CacheKey } from '@/utils/globalConstants'
import { LoginFromParam, LoginUserReq, LoginUserResp } from '@/models/auth/LoginUserModel'
import { TokenInfo } from '@/models/auth/TokenModel'
import Logger from '@/utils/Logger'
import { ProcessingStatus } from '@/config/processingStatus'
import { ClickCaptchaConf } from '@/components/ClickCaptcha/src/meta/conf'
import { ClickCaptchaData, ClickCaptchaDot } from '@/components/ClickCaptcha/src/meta/data'
import { CheckCaptchaReq, CheckPoint } from '@/models/auth/CaptchaModel'
import { useMessage } from '@/hooks/web/useMessage'

defineOptions({ name: 'LoginForm' })

const { t } = useI18n()
const message = useMessage()
const iconHouse = useIcon({ icon: 'ep:house' })
const iconAvatar = useIcon({ icon: 'ep:avatar' })
const iconLock = useIcon({ icon: 'ep:lock' })
// 登录表单实例
const formLogin = ref<FormInstance>()
const { validForm } = useFormValid(formLogin)
const { setLoginState, getLoginState } = useLoginState()
const { currentRoute, push } = useRouter()
const permissionStore = usePermissionStore()
const redirect = ref<string>('')
const loginLoading = ref(false)
// 验证码处理
const clickCaptchaInstance = ref()
const captchaData = reactive<ClickCaptchaData>({
  image: '',
  thumb: '',
  key: ''
})
const captchaEvents = {
  click(x: number, y: number): void {
    console.log('click', x, y)
  },
  confirm: async (dots: Array<ClickCaptchaDot>, reset: () => void) => {
    const reqParams = dots.map<CheckPoint>((item) => {
      return {
        X: item.x,
        Y: item.y
      }
    })
    const flag = await checkCaptchaImage(reqParams)
    if (!flag) {
      clickCaptchaInstance.value?.refresh()
      return
    }
    clickCaptchaInstance.value?.close()
    handleLogin()
  },
  async refresh() {
    await getCaptchaImage()
  },
  close(): void {
    console.log('close')
  }
}
const captchaConf = reactive<ClickCaptchaConf>({
  width: 400,
  height: 250
})

const getShow = computed(() => unref(getLoginState) === LoginStateEnum.LOGIN)

// 验证规则
const LoginRules = {
  tenantName: [required],
  username: [required],
  password: [required]
}

// 获取配置参数
const loginFormInitdata = reactive<any>({
  // 是否显示密码
  IsShowPassword: false,
  // 是否开启验证码
  CaptchaEnable: import.meta.env.VITE_APP_CAPTCHA_ENABLE as unknown as boolean,
  // 是否开启租户
  TenantEnable: import.meta.env.VITE_APP_TENANT_ENABLE as unknown as boolean,
  // 是否记住账号
  RememberMe: true
})

// 表单参数
const loginFromData = reactive<LoginFromParam>({
  UserName: import.meta.env.VITE_APP_USERNAME as unknown as string,
  PassWord: import.meta.env.VITE_APP_PASSWORD as unknown as string,
  CaptchaVerification: '',
  RememberMe: true,
  TenantName: import.meta.env.VITE_APP_TENANT_NAME as unknown as string
})

const socialList = [
  { icon: 'ant-design:wechat-filled', type: 30 },
  { icon: 'ant-design:dingtalk-circle-filled', type: 20 },
  { icon: 'ant-design:github-filled', type: 0 },
  { icon: 'ant-design:alipay-circle-filled', type: 0 }
]

// 获取验证码图片
const getCaptchaImage = async () => {
  const resp = await LoginApi.getCode({ ClickCaptchaType: 1 })
  if (!resp || resp.Status !== ProcessingStatus.ProcessingSuccess || !resp.Data.Ok) {
    message.warning(resp.Message)
    return
  }
  captchaData.key = resp.Data.CaptchaKey
  captchaData.image = resp.Data.CaptchaImage as unknown as string
  captchaData.thumb = resp.Data.CaptchCheckBase64Image as unknown as string
}

// 校检码是否正确
const checkCaptchaImage = async (checkPoints: Array<CheckPoint>): Promise<boolean> => {
  const reqParams: CheckCaptchaReq = {
    CaptchaKey: captchaData.key,
    CaptchaDots: checkPoints
  }
  const res = await LoginApi.reqCheck(reqParams)
  if (!res || !res.Data || res.Status !== ProcessingStatus.ProcessingSuccess) {
    message.warning(res.Message)
    return false
  }
  return res.Data.Ok
}

// 获取验证码
const getCode = async () => {
  // debugger
  const flag = await validForm()
  if (!flag) {
    return
  }
  clickCaptchaInstance.value.show && clickCaptchaInstance.value.show()
  await getCaptchaImage()
}

// 获取租户 ID
const getTenantId = async () => {
  if (!loginFormInitdata.TenantEnable) return
  const id = cache.readCache(CacheKey.TenantId)
  if (id) return
  const reqParam: QueryTenantByNameReq = {
    Name: loginFromData.TenantName as unknown as string
  }
  const result = await LoginApi.getTenantIdByName(reqParam)
  if (result && result.Status === ProcessingStatus.ProcessingSuccess && result.Data) {
    cache.writeCache(result.Data.Id, CacheKey.TenantId)
  }
}
// 获取登录表单缓存
const getRememberLoginData = () => {
  const loginCacheForm = cache.getLoginForm()
  if (!loginCacheForm) return
  loginFromData.UserName = loginCacheForm.UserName
    ? loginCacheForm.UserName
    : loginFromData.UserName
  loginFromData.PassWord = loginCacheForm.PassWord
    ? loginCacheForm.PassWord
    : loginFromData.PassWord
  loginFromData.RememberMe = loginCacheForm.RememberMe
  loginFromData.TenantName = loginCacheForm.TenantName
    ? loginCacheForm.TenantName
    : loginFromData.TenantName
}
// 根据域名，获得租户信息
const getTenantByWebsite = async () => {
  const website = location.host

  try {
    const res = await LoginApi.getTenantByWebsite(website)
    if (!res || res.Status !== 0) {
      return
    }
    loginFromData.TenantName = res.Data.Name || ''
    cache.writeCache(res.Data.Id as unknown as string, CacheKey.TenantId)
  } catch (err) {
    Logger.prettyError('【登录】getTenantByWebsite请求异常', err)
  }
}

// ElLoading.service 返回的实例
const loading = ref()
// 登录
const handleLogin = async () => {
  loginLoading.value = true
  try {
    await getTenantId()
    const loginDataLoginForm = { ...loginFromData }
    // 登录参数
    const loginReqParam = buildLoginParams(loginDataLoginForm)
    // 登录
    const res = await LoginApi.login(loginReqParam)
    // debugger
    if (!res || res.Status !== ProcessingStatus.ProcessingSuccess) {
      message.warning(res.Message)
      return
    }
    loading.value = ElLoading.service({
      lock: true,
      text: '正在加载系统中...',
      background: 'rgba(0, 0, 0, 0.7)'
    })
    // 记住账号
    handleRememberMe()
    cacheCurrentToken(res.Data)

    if (!redirect.value) {
      redirect.value = '/'
    }
    // 判断是否为SSO登录
    if (redirect.value.indexOf('sso') !== -1) {
      window.location.href = window.location.href.replace('/login?redirect=', '')
    } else {
      console.log('redirect', redirect.value || permissionStore.addRouters[0].path)
      push({ path: redirect.value || permissionStore.addRouters[0].path })
    }
  } finally {
    loginLoading.value = false
    if (loading.value) loading.value.close()
  }
}

// 缓存token
const cacheCurrentToken = (loginInfo: LoginUserResp) => {
  if (!loginInfo) return
  // 登录成功，保存 token
  const tokenInfo: TokenInfo = {
    AccessToken: loginInfo.Token,
    ExpiresIn: loginInfo.ExpiresIn,
    RefreshToken: loginInfo.RefreshToken as unknown as string
  }
  cache.setToken(tokenInfo)
}

// 处理记住账号和密码
const handleRememberMe = () => {
  if (!loginFromData.RememberMe) {
    cache.clearLoginForm()
    return
  }
  cache.setLoginForm(loginFromData)
}

// 构建登录请求参数
const buildLoginParams = (params: LoginFromParam): LoginUserReq => {
  const loginReqParam: LoginUserReq = {
    UserName: params.UserName,
    PassWord: params.PassWord,
    TenantId: params.TenantId as unknown as string,
    Captcha: ''
  }
  return loginReqParam
}

// 社交登录
const doSocialLogin = async (type: number) => {
  if (type === 0) {
    message.error('此方式未配置')
    return
  }
  loginLoading.value = true

  if (loginFormInitdata.TenantEnable) {
    // 尝试先通过 tenantName 获取租户
    await getTenantId()
    // 如果获取不到，则需要弹出提示，进行处理
    if (!cache.readCache(CacheKey.TenantId)) {
      try {
        const data = await message.prompt('请输入租户名称', t('common.reminder'))
        if (data?.action !== 'confirm') throw 'cancel'
        const res = await LoginApi.getTenantIdByName({ Name: data.value })
        if (!res || !res.Data) throw 'cancel'
        cache.writeCache(res.Data.Id, CacheKey.TenantId)
      } catch (error) {
        if (error === 'cancel') return
      } finally {
        loginLoading.value = false
      }
    }
  }
  // 计算 redirectUri
  // tricky: type、redirect需要先encode一次，否则钉钉回调会丢失。
  // 配合 Login/SocialLogin.vue#getUrlValue() 使用
  const redirectUri =
    location.origin +
    '/social-login?' +
    encodeURIComponent(`type=${type}&redirect=${redirect.value || '/'}`)

  // 进行跳转
  const res = await LoginApi.socialAuthRedirect({
    Type: type,
    Redirect: encodeURIComponent(redirectUri)
  })
  if (!res || res.Status !== ProcessingStatus.ProcessingSuccess) return
  window.location.href = res.Data.Url || '/'
}

watch(
  () => currentRoute.value,
  (route: RouteLocationNormalizedLoaded) => {
    redirect.value = route?.query?.redirect as string
  },
  {
    immediate: true
  }
)
onMounted(() => {
  getRememberLoginData()
  getTenantByWebsite()
})
</script>

<style lang="scss" scoped>
:deep(.anticon) {
  &:hover {
    color: var(--el-color-primary) !important;
  }
}

.login-code {
  float: right;
  width: 100%;
  height: 38px;

  img {
    width: 100%;
    height: auto;
    max-width: 100px;
    vertical-align: middle;
    cursor: pointer;
  }
}
</style>
